package arithmetic.encrypt.rsa;


import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Auther: luobiao
 * @Description: 享宇接口验签辅助类
 */
public class SignHelper {
    private static byte[] encryptOrDecrypt(Key key, byte[] content, int cipherType, EncryptArithmeticEnum arithmeticEnum) {
        if (key == null) {
            throw new RuntimeException("密钥不能为空, 请设置");
        }
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(arithmeticEnum.name());
            cipher.init(cipherType, key);
            byte[] output = cipher.doFinal(content);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("无此算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new RuntimeException("密钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException("明文长度非法");
        } catch (BadPaddingException e) {
            throw new RuntimeException("明文数据已损坏");
        }
    }

    private static Key loadKeyByStr(String keyStr, EncryptArithmeticEnum arithmeticEnum, int type) {
        try {
            byte[] buffer = Base64.decode(keyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(arithmeticEnum.name());
            EncodedKeySpec keySpec = null;
            Key key = null;
            if (type == 1) {
                keySpec = new X509EncodedKeySpec(buffer);
                key = keyFactory.generatePublic(keySpec);
            } else {
                keySpec = new PKCS8EncodedKeySpec(buffer);
                key = keyFactory.generatePrivate(keySpec);
            }
            return key;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("密钥非法");
        } catch (NullPointerException e) {
            throw new RuntimeException("密钥数据为空");
        }
    }

    /**
     * 私钥加密
     * @param keyStr 私钥
     * @param encryptContent 需要加密内容字节数组
     * @param arithmeticEnum 对称加密算法
     * @return 加密后的字节数组
     */
    public static byte[] privateEncrypt(String keyStr, byte[] encryptContent, EncryptArithmeticEnum arithmeticEnum) {
        Key key = loadKeyByStr(keyStr, arithmeticEnum, 0);
        return encryptOrDecrypt(key, encryptContent, Cipher.ENCRYPT_MODE, arithmeticEnum);
    }

    /**
     * 私钥解密
     * @param keyStr 私钥
     * @param decryptContent 需要解密内容字节数组
     * @param arithmeticEnum 对称加密算法
     * @return 解密后的字节数组
     */
    public static byte[] privateDecrypt(String keyStr, byte[] decryptContent, EncryptArithmeticEnum arithmeticEnum) {
        Key key = loadKeyByStr(keyStr, arithmeticEnum, 0);
        return encryptOrDecrypt(key, decryptContent, Cipher.DECRYPT_MODE, arithmeticEnum);
    }

    /**
     * 公钥加密
     * @param keyStr 公钥
     * @param encryptContent 需要加密内容字节数组
     * @param arithmeticEnum 对称加密算法
     * @return 加密后的字节数组
     */
    public static byte[] publicEncrypt(String keyStr, byte[] encryptContent, EncryptArithmeticEnum arithmeticEnum) {
        Key key = loadKeyByStr(keyStr, arithmeticEnum, 1);
        return encryptOrDecrypt(key, encryptContent, Cipher.ENCRYPT_MODE, arithmeticEnum);
    }

    /**
     * 公钥解密
     * @param keyStr 公钥
     * @param decryptContent 需要解密内容字节数组
     * @param arithmeticEnum 对称加密算法
     * @return 解密后的字节数组
     */
    public static byte[] publicDecrypt(String keyStr, byte[] decryptContent, EncryptArithmeticEnum arithmeticEnum) {
        Key key = loadKeyByStr(keyStr, arithmeticEnum, 1);
        return encryptOrDecrypt(key, decryptContent, Cipher.DECRYPT_MODE, arithmeticEnum);
    }

    /**
     * 私钥签名
     * @param privateKey 私钥
     * @param content 需要签名的内容
     * @param signArithmeticEnum 签名算法
     * @param encryptArithmeticEnum 非对称加密算法
     * @return 签名后经过base64的字符串
     */
    public static String sign(String privateKey,String content,SignArithmeticEnum signArithmeticEnum,EncryptArithmeticEnum encryptArithmeticEnum){
        try{
            PKCS8EncodedKeySpec priPKCS8    = new PKCS8EncodedKeySpec( Base64.decode(privateKey) );
            KeyFactory keyf = KeyFactory.getInstance(encryptArithmeticEnum.name());
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            Signature signature = Signature.getInstance(signArithmeticEnum.name());
            signature.initSign(priKey);
            signature.update( content.getBytes());
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过公钥验证签名
     * @param publicKey 公钥
     * @param content 原始内容
     * @param sign 签名
     * @param signArithmeticEnum 使用的摘要算法
     * @param encryptArithmeticEnum 使用的非对称加密算法
     * @return 签名是否校验通过
     */
    public static boolean doCheck(String publicKey,String content, String sign,SignArithmeticEnum signArithmeticEnum,EncryptArithmeticEnum encryptArithmeticEnum){
        try{
            KeyFactory keyFactory = KeyFactory.getInstance(encryptArithmeticEnum.name());
            byte[] encodedKey = Base64.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

            Signature signature = Signature.getInstance(signArithmeticEnum.name());
            signature.initVerify(pubKey);
            signature.update( content.getBytes() );
            boolean bverify = signature.verify( Base64.decode(sign) );
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}

